Skip to content

Conversation

@aaronburtle
Copy link
Contributor

Why make this change?

Adds AKV variable replacement and expands our design for doing variable replacements to be more extensible when new variable replacement logic is added.

Closes #2708
Closes #2748
Related to #2863

What is this change?

Change the way that variable replacement is handled to instead of simply using a bool to indicate that we want env variable replacement, we add a class which holds all of the replacement settings. This will hold whether or not we will do replacement for each kind of variable that we will handle replacement for during deserialization. We also include the replacement failure mode, and put the logic for handling the replacements into a strategy dictionary which pairs the replacement variable type with the strategy for doing that replacement.

Because Azure Key Vault secret replacement requires having the retry and connection settings in order to do the AKV replacement, we must do a first pass where we only do non-AKV replacement and get the required settings so that if AKV replacement is used we have the required settings to do that replacement.

We also have to keep in mind that the legacy of the Configuration Controller will ignore all variable replacement, so we construct the replacement settings for this code path to not use any variable replacement at all.

How was this tested?

We have updated the logic for the tests to use the new system, however manual testing using an actual AKV is still required.

Sample Request(s)

  • Example REST and/or GraphQL request to demonstrate modifications
  • Example of CLI usage to demonstrate modifications


if (!string.IsNullOrEmpty(json) && TryParseConfig(json, out RuntimeConfig, connectionString: _connectionString, replaceEnvVar: replaceEnvVar))
if (!string.IsNullOrEmpty(json) && TryParseConfig(json, out RuntimeConfig,
new DeserializationVariableReplacementSettings(azureKeyVaultOptions: null, doReplaceEnvVar: true, doReplaceAKVVar: true), logger: null, connectionString: _connectionString))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to delete the parameter replaceEnvVar from the TryLoadConfig function since we don't use it anymore

Copy link
Contributor

@RubenCerna2079 RubenCerna2079 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to get rid of envVar in some places and resolve missing logic inside of an if statement.

Comment on lines +141 to +144
clientOptions.Retry.MaxRetries = options.RetryPolicy.MaxCount ?? 3;
clientOptions.Retry.Delay = TimeSpan.FromSeconds(options.RetryPolicy.DelaySeconds ?? 1);
clientOptions.Retry.MaxDelay = TimeSpan.FromSeconds(options.RetryPolicy.MaxDelaySeconds ?? 16);
clientOptions.Retry.NetworkTimeout = TimeSpan.FromSeconds(options.RetryPolicy.NetworkTimeoutSeconds ?? 30);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we use the DEFAULT variables found in AKVRetryPolicyOptions instead of using numbers?

Comment on lines +173 to +179
catch
{
// If we can't extract AKV options, return null and proceed without AKV variable replacement
return null;
}

return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Returning the nulls like this seems a bit redundant, do you think there is a better way to do this?

public class DeserializationVariableReplacementSettings
{
public bool DoReplaceEnvVar { get; set; } = true;
public bool DoReplaceAKVVar { get; set; } = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public bool DoReplaceAKVVar { get; set; } = true;
public bool DoReplaceAkvVar { get; set; } = true;

Comment on lines +153 to +154
envFailureMode: replacementFailureMode);
options.Converters.Add(new StringJsonConverterFactory(envOnlySettings));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
envFailureMode: replacementFailureMode);
options.Converters.Add(new StringJsonConverterFactory(envOnlySettings));
envFailureMode: replacementFailureMode);
options.Converters.Add(new StringJsonConverterFactory(envOnlySettings));

Comment on lines +167 to +171
using JsonDocument doc = JsonDocument.Parse(json);
if (doc.RootElement.TryGetProperty("azure-key-vault", out JsonElement akvElement))
{
return JsonSerializer.Deserialize<AzureKeyVaultOptions>(akvElement.GetRawText(), options);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we want to deserialize the azure-key-vault here? Don't we already do that with the ConverterFactory?

Copy link
Contributor

@souvikghosh04 souvikghosh04 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comments

catch
{
// If we can't extract AKV options, return null and proceed without AKV variable replacement
return null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything explicitly handled in catch. It returns null as same outside the catch

updatedConnection = GetConnectionStringWithApplicationName(connectionValue);
}
else if (ds.DatabaseType is DatabaseType.PostgreSQL && replaceEnvVar)
else if (ds.DatabaseType is DatabaseType.PostgreSQL && replacementSettings?.DoReplaceEnvVar == true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: replacementSettings?.DoReplaceEnvVar is true ?

clientOptions.Retry.Mode = retryMode;
clientOptions.Retry.MaxRetries = options.RetryPolicy.MaxCount ?? 3;
clientOptions.Retry.Delay = TimeSpan.FromSeconds(options.RetryPolicy.DelaySeconds ?? 1);
clientOptions.Retry.MaxDelay = TimeSpan.FromSeconds(options.RetryPolicy.MaxDelaySeconds ?? 16);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the MaxDelay be more than the NetworkTimeout? also are these configurable or going to be fixed always?

Copy link
Collaborator

@Aniruddh25 Aniruddh25 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes look good to me. This is a good refactor. However, unit testing still remains and so does the test in AKV environment.

/// <returns>AzureKeyVaultOptions if present, null otherwise.</returns>
private static AzureKeyVaultOptions? ExtractAzureKeyVaultOptions(string json,
bool enableEnvReplacement,
Azure.DataApiBuilder.Config.Converters.EnvironmentVariableReplacementFailureMode replacementFailureMode = Azure.DataApiBuilder.Config.Converters.EnvironmentVariableReplacementFailureMode.Throw)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: do not need the prefix Azure.DataApiBuilder.Config since it is part of the same namespace.

runtimeConfig.ToJson(),
out RuntimeConfig updatedRuntimeConfig,
replaceEnvVar: true);
new ());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: name the argument like

replacementSettings: new()

{
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(
GetModifiedJsonString(repValues, @"""postgresql"""), out expectedConfig, replaceEnvVar: replaceEnvVar),
GetModifiedJsonString(repValues, @"""postgresql"""), out expectedConfig, replacementSettings: new DeserializationVariableReplacementSettings(azureKeyVaultOptions: null, doReplaceEnvVar: replaceEnvVar, doReplaceAKVVar: true)),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add similar tests for AKV replacement checks

throw new JsonException("Invalid AzureKeyVaultOptions format");
}

public override void Write(Utf8JsonWriter writer, AzureKeyVaultOptions value, JsonSerializerOptions options)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What changed in this file?

connectionString: connectionString,
accessToken: CONFIG_TOKEN);
accessToken: CONFIG_TOKEN,
new());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please name the argument - what is this new() supplied to?

connectionString: connectionString,
accessToken: CONFIG_TOKEN);
accessToken: CONFIG_TOKEN,
new());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name the parameter, and apply the same suggestion every other point

connectionString: connectionString,
accessToken: CONFIG_TOKEN);
accessToken: CONFIG_TOKEN,
new());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name the argument

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support .akv files just like .env files. [Enhancement]: Support AKV

5 participants